home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1997 August / Walnut Creek CDROM.7z / LISTINGS / V_13_04 / CHAPMAN.ZIP / ERRMGR.CPP < prev    next >
Encoding:
Text File  |  1994-03-02  |  9.7 KB  |  340 lines

  1. Listing 6.
  2.  
  3. /* errmgr.cpp - error manager */
  4. /* has been trimmed to fit; see the monthly code */
  5. /* disk for the complete source. */
  6.  
  7. #include <stdio.h>
  8. #include <ctype.h>
  9. #include <stdlib.h>
  10. #include <stdarg.h>
  11. #include <string.h>
  12. #include "complain.hpp"
  13. #include "utils.hpp"
  14. #include "c_failur.hpp"
  15. #include "errmgr.hpp"
  16.  
  17. /* an element in the list of complaint dictionaries: */
  18.  
  19. class error_dict_list {
  20.     public:
  21.         error_dict_list(error_dict_list *curr,
  22.                         complaint_dict *new_dict);
  23.         error_dict_list *prev;
  24.         complaint_dict *dict;
  25. };
  26.  
  27. error_mgr err_mgr;
  28.  
  29. /* is_set_up is set when err_mgr is initialized. no */
  30. /* other error manager can be active. */
  31.  
  32. int error_mgr::is_set_up = 0;
  33.  
  34. /* asserts_off is 0 when assertions are to be checked. */
  35.  
  36. #ifndef NDEBUG
  37. int error_mgr::asserts_off = 0;
  38. #else
  39. int error_mgr::asserts_off = 1;
  40. #endif
  41.  
  42. failure_handler *error_mgr::curr_handler = 0,
  43.                 *error_mgr::default_handler = 0;
  44. error_dict_list *error_mgr::error_dicts = 0;
  45. ptr_stack *error_mgr::handler_stack = 0;
  46.  
  47. static char line[512];  /* should be managed buffer */
  48.  
  49. static int proper_format(const char *pgm_text,
  50.                          const char *user_text);
  51. static char format_specifier(const char **fmt);
  52.  
  53. error_mgr::error_mgr(void)      /* constructor */
  54. {
  55.     setup();
  56. }
  57.  
  58. error_mgr::~error_mgr(void)     /* destructor */
  59. {
  60.     delete default_handler;
  61.     delete handler_stack;
  62. }
  63.  
  64. void error_mgr::setup(void)
  65. {
  66.     /* create default handler and empty dictionary */
  67.     /* list, allocate stack for failure_handlers, */
  68.     /* make sure no other error_mgr is present. */
  69.     if (this != &err_mgr)       /* just in case... */
  70.         err_mgr.fail("Duplicate error_mgr defined.\n");
  71.     if (is_set_up)
  72.         return;
  73.     is_set_up = 1;
  74.     curr_handler = default_handler =
  75.                    new failure_handler;
  76.     handler_stack = new ptr_stack;
  77. }
  78.  
  79. int error_mgr::define_dictionary(const char *filename)
  80. {
  81.     /* add a new dictionary to the list. it's */
  82.     /* ignored if any errors are found. */
  83.     counting_failure_handler *our_handler;
  84.     complaint_dict *new_dict;
  85.  
  86.     setup();
  87.     /* we count errors in the file with our_handler. */
  88.     our_handler = new counting_failure_handler;
  89.     new_dict = new complaint_dict(filename);
  90.     if (our_handler->errors_logged() ||
  91.         our_handler->warns_logged()) {
  92.         delete new_dict;
  93.         warn("$bad_error_dict: dictionary file \"%s\" "
  94.              "has errors - will not be used\n",
  95.              filename);
  96.         delete our_handler;
  97.         return 0;
  98.     }
  99.     error_dicts = new error_dict_list(
  100.                            error_dicts,new_dict);
  101.     delete our_handler;
  102.     return 1;
  103. }
  104.  
  105. failure_handler *error_mgr::define_handler(
  106.                       failure_handler *new_handler)
  107. {
  108.     /* install a new error handler, pushing the */
  109.     /* previous handler onto a stack. */
  110.     failure_handler *old_handler;
  111.  
  112.     setup();
  113.     handler_stack->push(curr_handler);
  114.     old_handler = curr_handler;
  115.     if (new_handler == 0)       /* bomb-proof */
  116.         curr_handler = default_handler;
  117.     else
  118.         curr_handler = new_handler;
  119.     return old_handler;
  120. }
  121.  
  122. failure_handler *error_mgr::restore_handler(void)
  123. {
  124.     /* return to the previous error handler. */
  125.     failure_handler *old_handler;
  126.  
  127.     setup();
  128.     old_handler = curr_handler;
  129.     curr_handler = (failure_handler *)
  130.                          (handler_stack->pop());
  131.     if (curr_handler == 0)
  132.         curr_handler = default_handler;
  133.     return old_handler;
  134. }
  135.  
  136. const char *error_mgr::message(
  137.                        const char *fmt,char *msg_line,
  138.                        int linelen)
  139. {
  140.     /* search for the replacement text, writing */
  141.     /* it into msg_line if found. return a pointer */
  142.     /* to the start of the message, ready to print. */
  143.     const char *new_fmt,*key,*keystart;
  144.     char *key_alloc,*buf;
  145.     int keylen;
  146.  
  147.     /* this routine calls setup() for fail(), */
  148.     /* vfail(), etc. */
  149.     setup();
  150.     ASSERT(fmt != msg_line);
  151.     msg_line[0] = '\0';
  152.     if (*fmt != '$') {          /* not keyed? */
  153.         strncpy(msg_line,fmt,linelen);
  154.         msg_line[linelen - 1] = '\0';
  155.         return msg_line;
  156.     }
  157.  
  158.     /* extract the key. */
  159.     key = ++fmt;                /* skip '$' */
  160.     key += skipblanks(key);
  161.     keylen = skip_ident(key);   /* fetch key name */
  162.     new_fmt = key + keylen;     /* skip key name */
  163.     new_fmt += skipblanks(key); /* skip to ':' */
  164.  
  165.     /* error text here is not replaceable - */
  166.     /* recursive calls would be dangerous. */
  167.     if (keylen == 0 || *new_fmt != ':') {
  168.         sprintf(msg_line,"Keyed error message format "
  169.                 "string is malformed\n%s",fmt);
  170.     }
  171.     else {
  172.         key_alloc = newstring(key,keylen);
  173.         ++new_fmt;              /* skip ':' */
  174.         new_fmt += skipblanks(new_fmt);
  175.         if (find_replacement(key_alloc,msg_line,
  176.                              linelen)) {
  177.             if (!proper_format(new_fmt,msg_line)) {
  178.                 /* invalid; dictionary text left at */
  179.                 /* front of resulting error message. */
  180.                 buf = msg_line + strlen(msg_line);
  181.                 sprintf(buf,"Replacement text for "
  182.                         "keyed error message is "
  183.                         "malformed\n%s",fmt);
  184.             }
  185.         }
  186.         else {                  /* use program version */
  187.             strncpy(msg_line,new_fmt,linelen);
  188.             msg_line[linelen - 1] = '\0';
  189.         }
  190.         free(key_alloc);
  191.     }
  192.     return msg_line;
  193. }
  194.  
  195. int error_mgr::find_replacement(
  196.                     const char *key,char *msg_line,
  197.                     int linelen)
  198. {
  199.     /* search for message replacement. returns 1 */
  200.     /* if found, 0 if not. */
  201.     error_dict_list *curr_dict;
  202.  
  203.     for (curr_dict = error_dicts; curr_dict != 0;
  204.          curr_dict = curr_dict->prev)
  205.         if (curr_dict->dict->key_defined(key))
  206.             break;
  207.     return curr_dict != 0 &&
  208.            curr_dict->dict->complaint_text(
  209.                                key,msg_line,linelen);
  210. }
  211.  
  212. static int proper_format(const char *pgm_text,
  213.                          const char *user_text)
  214. {
  215.     /* return 1 if the printf() format specifiers */
  216.     /* in user_text match the ones in pgm_text. */
  217.     char pgm_fmt,user_fmt;
  218.  
  219.     do {
  220.         pgm_fmt = format_specifier(&pgm_text);
  221.         user_fmt = format_specifier(&user_text);
  222.         if (pgm_fmt != user_fmt)
  223.             return 0;
  224.     } while (*pgm_text || *user_text);
  225.     return 1;
  226. }
  227.  
  228. static char format_specifier(const char **fmt)
  229. {
  230.     /* skip to the next '%' specifier (if any) */
  231.     /* and return a unique letter indicating its */
  232.     /* type. advances *fmt past the specifier. */
  233.     const char *s = *fmt;       /* *s vs. **fmt */
  234.     char c;
  235.  
  236.     for (;;) {                  /* skip to '%' */
  237.         while (*s && *s != '%')
  238.             ++s;
  239.         if (*s == '\0')
  240.             break;
  241.         ++s;                    /* skip '%' */
  242.         if (*s == '%')          /* "%%" prints '%' */
  243.             ++s;                /* not specifier */
  244.         else
  245.             break;
  246.     }
  247.     if (*s == '\0') {
  248.         *fmt = s;               /* update caller var */
  249.         return '\0';
  250.     }
  251.  
  252.     /* skip to the specifier letter. */
  253.     while (*s && !isspace(*s) && !isalpha(*s))
  254.         ++s;
  255.     if (!isalpha(*s)) {
  256.         *fmt = s;               /* update caller var */
  257.         return '\0';            /* bad specifier */
  258.     }
  259.  
  260.     /* any 'l' is assumed to be long. */
  261.     if (*s == 'l' && isalpha(*(s + 1))) {
  262.         *fmt = s + 2;           /* skip l, specifier */
  263.         return 'l';
  264.     }
  265.  
  266.     /* map the specifier into a canonical value. */
  267.     c = tolower(*s);
  268.     *fmt = s + 1;               /* update caller var */
  269.     switch(c) {
  270.     case 'e': case 'f': case 'g':
  271.         return 'e';             /* floating point */
  272.     case 'o': case 'u': case 'x':
  273.     case 'd': case 'i': case 'b':
  274.         return 'd';             /* int */
  275.     default:                    /* all others map */
  276.         return c;               /* to themselves */
  277.     }  /* end of switch(c) */
  278.     /* NOTREACHED */
  279. }
  280.  
  281. void error_mgr::fail(const char *fmt,...)
  282. {
  283.     /* print the message and exit. */
  284.     va_list ap;
  285.  
  286.     va_start(ap,fmt);
  287.     vfail(fmt,ap);              /* won't return */
  288.     /* NOTREACHED */
  289.     va_end(ap);
  290. }
  291.  
  292. void error_mgr::vfail(const char *fmt,va_list ap)
  293. {
  294.     /* fail() with a va_list already built. if */
  295.     /* the handler doesn't exit we force it. */
  296.     fmt = message(fmt,line,sizeof(line));
  297.     curr_handler->fail(fmt,ap);
  298.     exit(EXIT_FAILURE);         /* just in case! */
  299. }
  300.  
  301. void error_mgr::error(const char *fmt,...)
  302. {
  303.     /* print the message and ask for permission to */
  304.     /* continue. */
  305.     va_list ap;
  306.  
  307.     va_start(ap,fmt);
  308.     verror(fmt,ap);
  309.     va_end(ap);
  310. }
  311.  
  312. void error_mgr::verror(const char *fmt,va_list ap)
  313. {
  314.     /* error() with a va_list already built. */
  315.     /* we replace the message if possible. */
  316.     fmt = message(fmt,line,sizeof(line));
  317.     curr_handler->error(fmt,ap);
  318. }
  319.  
  320. int error_mgr::set_assert_flag(int asserts_on)
  321. {
  322.     /* enable or disable assertions at run time. */
  323.     int retval = asserts_off;
  324.  
  325.     /* set to opposite state because we want to */
  326.     /* short-circuit assertion evaluation. */
  327.     asserts_off = !asserts_on;
  328.     return !retval;
  329. }
  330.  
  331. int error_mgr::assert_failed(
  332.                    const char *exp,const char *fname,
  333.                    unsigned linenum)
  334. {
  335.     /* an assertion has failed. */
  336.     error("Assertion failed - file %s, line %u:\n%s\n",
  337.           fname,linenum,exp);
  338.     return 1;                   /* needed for macro */
  339. }
  340.